package com.ccteam.fluidmusic.view.components

import android.content.Context
import android.database.Observable
import android.util.AttributeSet
import android.view.View
import android.widget.ImageView
import androidx.core.content.res.use
import com.bumptech.glide.Glide
import com.ccteam.fluidmusic.R
import com.google.android.material.card.MaterialCardView
import com.orhanobut.logger.Logger
import kotlin.math.max
import kotlin.math.min

/**
 * @author Xiaoc
 * @since 2021/3/24
 *
 * 圆角图片宫格自定义视图
 *
 * 宫格格式如下：
 * 当图片小于3张时，根据宽度高度自适应铺满
 * 当图片大于等于3张后，进行九宫格等内容的自适应，处理为九宫格样式
 */
class CornerImageWrapperView: MaterialCardView {

    companion object{
        const val TAG = "CornerImageWrapperView.kotlin"
    }

    private var config = Config()

    /**
     * 列数与行数
     */
    private var columnCount = 0
    private var rowCount = 0

    /**
     * 每一个图片宫格的宽度和高度
     */
    private var gridWidth = 0
    private var gridHeight = 0

    private var imageViews: MutableList<ImageView> = mutableListOf()

    /**
     * 图片地址列表缓存，用于缓存ImageView，以复用
     */
    private var mCacheSize: Int = 0

    /**
     * 当前适配器暂存，默认为null
     */
    private var mAdapter: ImageWrapperAdapter<*>? = null

    /**
     * 观察器，用于Adapter通知数据变化进行操作
     */
    private val mObserver = CornerImageWrapperViewDataObserver()

    /**
     * 边缘圆角大小（px）
     */
    private var cornerEdgeSize: Int = 20

    constructor(context: Context): this(context,null)

    constructor(context: Context, attrs: AttributeSet?): this(context,attrs,0)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context,attrs,defStyleAttr){
        cardElevation = 0.0f
        val ta = context.obtainStyledAttributes(attrs, R.styleable.CornerImageWrapperView)
        ta.use {
            cornerEdgeSize = ta.getDimensionPixelSize(R.styleable.CornerImageWrapperView_cornerEdgeSize,20)
        }
        setCornerSize(cornerEdgeSize)
    }

    /**
     * 测量布局高度，通过判断图片数量进行高度计算
     */
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)

        var width = MeasureSpec.getSize(widthMeasureSpec)
        var height = 0
        val totalWidth = width - paddingLeft - paddingRight
        // 当图片列表有内容时，开始进行布局计算
        if(mAdapter != null){
            val urlList = mAdapter!!.mImageList
            if(urlList.isNotEmpty()){
                when(val imageCount = urlList.size){
                    1 ->{
                        // 如果只有一张图片，则宫格宽度为整个容器的宽度，高度为具体比值
                        gridWidth = totalWidth
                        gridHeight = (gridWidth / config.singleImageRatio).toInt()
                    }
                    else ->{
                        // 其他情况时，宫格宽度与高度相同，且按照图片数量进行排布
                        val c = if(imageCount ==  2){
                            1
                        } else {
                            2
                        }
                        gridWidth = (totalWidth - config.gridSpacing * c) / min(imageCount,3)
                        gridHeight = gridWidth
                    }
                }
                // 整个布局的总宽度和总高度则是每个宫格与对应行列数的和+对应间距
                width = gridWidth * columnCount + config.gridSpacing * (columnCount - 1) + paddingLeft + paddingRight
                height = gridHeight * rowCount + config.gridSpacing * (rowCount - 1) + paddingTop + paddingBottom
            }
        } else {
            Logger.d("$TAG: 没有adapter适配器")
        }
        setMeasuredDimension(width,height)
    }

    /**
     * 给布局中的每个组件(图片控件)进行宽高测量应用
     */
    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        if(mAdapter == null){
            Logger.d("$TAG: 没有adapter适配器")
            return
        }

        mAdapter!!.mImageList.forEachIndexed { index, _ ->
            val childrenView = getChildAt(index)

            val rowNum = index / columnCount
            val columnNum = index % columnCount
            val left = (gridWidth + config.gridSpacing) * columnNum + paddingLeft
            val top = (gridHeight + config.gridSpacing) * rowNum + paddingTop
            val right = left + gridWidth
            val bottom = top + gridHeight
            // 设置图片ChildrenView大小
            childrenView.layout(left,top,right,bottom)
        }
    }

    fun setCornerSize(cornerSize: Int){
        radius = cornerSize.toFloat()
    }

    /**
     * 得到适配器
     */
    fun getAdapter(): ImageWrapperAdapter<*>?{
        return mAdapter
    }

    /**
     * 设置适配器
     * 设置适配器会进行适配器监听数据变化
     * 并且会进行一次图片测量显示，如果adapter中默认不存在图片，则不进行绘制
     */
    fun setAdapter(adapter: ImageWrapperAdapter<*>){
        handleAdapterInternal(adapter)
        handleImage()
    }

    /**
     * 处理Adapter适配器
     * 主要是对适配器数据设置监听者
     * 取消原来的监听者并设置新的适配器的监听
     */
    private fun handleAdapterInternal(adapter: ImageWrapperAdapter<*>?){
        mAdapter?.unregisterAdapterDataObserver(mObserver)
        mAdapter = adapter
        adapter?.registerAdapterDataObserver(mObserver)
    }

    /**
     * 处理图片内容
     */
    private fun handleImage(){
        if(mAdapter == null){
            return
        }

        val urlList = mAdapter!!.mImageList

        val imageCount = urlList.size

        // 根据图片数量不同行列数
        rowCount = imageCount / 3 + if(imageCount % 3 == 0){0} else {1}

        // 列最少1列，最多3列
        columnCount = max(min(3,imageCount),1)

        if(urlList.isNullOrEmpty()){
            // 当图片列表为空时，清除布局中的所有View
            removeAllViews()
        } else {
            // 如果原来列表不为空，则进行对应的增删布局操作
            val oldViewCount = mCacheSize
            if(oldViewCount > imageCount){
                removeViews(imageCount,oldViewCount - imageCount)
            } else if(oldViewCount < imageCount){
                for(index in oldViewCount until imageCount){
                    val iv = reUseImageView(index) ?: return
                    if(iv.parent != null){
                        removeView(iv)
                    }
                    addView(iv,generateDefaultLayoutParams())
                }
            }
        }

        mCacheSize = urlList.size

        // 重新绘制布局
        requestLayout()

        // 进行图片加载处理
        mAdapter!!.mImageList.forEachIndexed { index, _ ->
            val childrenView = getChildAt(index) as ImageView
            mAdapter!!.handleImageView(childrenView,index)
        }
    }

    /**
     * 重用ImageView
     * 避免每次创建ImageView，这里重用ImageView
     * 返回 null 代表Adapter为空
     */
    private fun reUseImageView(position: Int): ImageView?{
        return if(position < imageViews.size){
            // 复用对应图片的内容
            imageViews[position]
        } else {
            if(mAdapter != null){
                val imageView = mAdapter!!.generateImageView(context,config)
                imageViews.add(imageView)
                imageView
            } else {
                null
            }
        }
    }

    /**
     * 默认实现的图片包装器适配器
     * 它封装了基本方法，避免自行书写
     */
    abstract class ImageWrapperAdapter<E>(
        private val imageEventListener: ImageEventListener<E>
    ){
        /**
         * 数据观察者
         */
        private val mObservable = AdapterDataObservable()

        /**
         * 图片存储列表
         */
        internal val mImageList: MutableList<E> = mutableListOf()

        /**
         * 生成ImageView的策略
         * @return ImageView
         */
        internal fun generateImageView(context: Context, config: Config): ImageView {
            val imageView = ImageView(context)
            imageView.scaleType = config.imageScaleType
            return imageView
        }

        abstract fun getTransformImageUrl(imageInfo: E): String

        /**
         * 设置图片列表数据，该操作会通知监听者数据更改进行重新绘制
         */
        fun subImageList(imageList: List<E>) {
            mImageList.clear()
            mImageList.addAll(imageList)
            mObservable.notifyChanged()
        }

        /**
         * 处理ImageView
         * 将图片进行加载并设置监听等操作
         */
        internal fun handleImageView(imageView: ImageView,position: Int) {
            Glide.with(imageView)
                .load(getTransformImageUrl(mImageList[position]))
                .into(imageView)
            imageView.setOnClickListener {
                imageEventListener.imageClick(it,position,mImageList)
            }
        }

        /**
         * 取消注册监听者
         */
        internal fun unregisterAdapterDataObserver(observer: AdapterDataObserver){
            mObservable.unregisterObserver(observer)
        }

        /**
         * 设置监听者
         */
        internal fun registerAdapterDataObserver(observer: AdapterDataObserver){
            mObservable.registerObserver(observer)
        }
    }

    /**
     * 图片事件发生器
     */
    interface ImageEventListener<E>{

        /**
         * 当图片被点击后触发
         * @param view 视图
         * @param position 当前位置
         * @param imageList 图片列表
         */
        fun imageClick(view: View,position: Int,imageList: List<E>)
    }

    /**
     * 适配器数据观察器
     * 一旦有观察者注册到观察器中，则在观察器调用了具体方法后
     * 会通知所有观察者进行对应操作
     */
    private class AdapterDataObservable: Observable<AdapterDataObserver>() {

        fun notifyChanged(){
            mObservers.forEach {
                it.onChanged()
            }
        }
    }

    /**
     * 数据变化观察者
     */
    private inner class CornerImageWrapperViewDataObserver: AdapterDataObserver() {

        /**
         * 当数据发生改变后，重新进行图片绘制
         */
        override fun onChanged() {
            handleImage()
        }
    }

    open class AdapterDataObserver{
        open fun onChanged(){
            // Do Nothing
        }
    }


    /**
     * 一些设置配置数据
     */
    data class Config(

        /**
         * 图片缩放模式
         */
        val imageScaleType: ImageView.ScaleType = ImageView.ScaleType.CENTER_CROP,

        /**
         * 宫格间的空隙距离(dp)
         */
        val gridSpacing: Int = 8,

        /**
         * 单个图片时高度比例
         */
        val singleImageRatio: Float = 1.5f,
    )


}